/*
MAXCLIENT-ben definiált maximális számú klienssel összeköttetés létesítése
A klienseket láncolt listában tárolja
Kilépő klienseket helyesen lekezeli, a láncolt listából törli
A szerver feladata kliensek által beregisztrált szolgáltatások tárolása,
és ezekről információ nyújtása érdeklődő kliensek számára.
Bármely kliens 3 parancsot küldhet, melyek a következők:
REG: ez a parancs arra szolgál, hogy a REG parancs után – egymástól space-szel elválasztott bármennyi –
szolgáltatást regisztráljon a kliens a szervernél (pl. “REG ftp smtp pop3”).
A művelet sikeréről a kérő klienst értesítjük.
DEL: ez a parancs arra szolgál, hogy a DEL parancs után – egymástól space-szel elválasztott bármennyi –
szolgáltatását törölje a kliens (pl. “DEL ftp smtp”). A művelet sikeréről a kérő klienst értesítjük.
Ha van a kérésben olyan szolgáltatás, melyet a kliens nem regisztrált be,
arról a kliens hibaüzenetet kap, de a kérés többi műveletének sikerességét ez nem befolyásolja.
DISCOVER: ez a parancs arra szolgál, hogy a kliens a DISCOVER parancs után –
egymástól space-szel elválasztott bármennyi – szolgáltatást felfedezhessen.
Válaszként az kérésben szereplő szolgáltatásokat nyújtó összes kliens IP címét kapja.
Ha nincs a szolgáltatást nyújtó kliens, arról is értesítést kap.

+ 1 jegy:
Minden kliens a beregisztrált szolgáltatásának megnevezése után -p kapcsolóval annak portját is megadja,
s a felfedezés során a szerver az IP cím mellett ezt is visszaadja. (pl. “REG ftp -p1322 smtp -p25”)

+ 1 jegy:
Minden kliens a beregisztrált szolgáltatásának megnevezése után -t kapcsolóval annak lejáratát is megadja másodpercben,
s ha a beregisztrálástól számítva ez az idő letelik, a szerver törli az adott szolgáltatást.

+ 1 jegy:
„?” paracsra a kérdő kliens visszakapja a szervernél beregisztrált összes szolgáltatás nevét és az azt nyújtó IP címét.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>

#define MAXC 2
#define PORT "2222"

struct szolg{
	char nev[10];
	char port[10];
	struct szolg* next;
};

struct kliens{
	int fd;
	char ip[200];
	char port[10];
	struct szolg* szolgok;
	struct kliens* next;
	int szolgszam;
};
struct kliens* kliensek=NULL;
int kliensszam=0;

fd_set master;
int fdmax;
int listensocket;

void ktorol(int i){
	struct kliens* kl;
	if (i==kliensek->fd) {
		kl=kliensek;
		kliensek=kliensek->next;
	} else {
		kl=kliensek->next;
		struct kliens* elozo=kliensek;
		while(kl->fd!=i){
			elozo=kl;
			kl=kl->next;
		}
		elozo->next=kl->next;
	}
	FD_CLR(i,&master);
	int j;
	int ujfdmax;
	for (j = 0; j < fdmax; ++j) {
		if (FD_ISSET(j,&master)) {
			ujfdmax=j;
		}
	}
	fdmax=ujfdmax;
	close(i);
	printf("torolve: %d\n",i);
	fflush(stdout);
	free(kl);
	kliensszam--;
}



void msg(int i,char* c){
	char uz[200];
	memset(uz,'\0',200);
	strcpy(uz,c);
	send(i,uz,strlen(uz),0);
}

void kelutasit(int i){
	msg(i,"MAXC\n");
	close(i);
}

void kfelvesz(int i,struct sockaddr sa){
	struct kliens* kl=(struct kliens*)malloc(sizeof (struct kliens));
	kl->fd=i;
	kl->next=kliensek;
	kl->szolgok=NULL;
	kl->szolgszam=0;
	kliensek=kl;
	FD_SET(i,&master);
	if (i>fdmax) {
		fdmax=i;
	}
	kliensszam++;
}

void szolgfelvesz(int i, char szolgnev[10]){
	struct kliens* kl;
	kl=kliensek;
	while(kl->fd!=i){
		kl=kl->next;
	}
	struct szolg* sz=(struct szolg*)malloc(sizeof (struct szolg));

	sprintf(sz->nev,"%s",szolgnev);
	sz->next=kl->szolgok;
	kl->szolgok=sz;
	kl->szolgszam++;

	msg(i,"rendben\n");
}

void szolgtorol(int i, char szolgnev[10]){
	struct kliens* kl;
	kl=kliensek;
	while(kl->fd!=i){
		kl=kl->next;
	}
	if(kl->szolgok==NULL){
		msg(i,"nem lehet torolni\n");
		return;
	}
	int torolve=0;
	struct szolg* sz;
	if (strncmp(kl->szolgok->nev,szolgnev,strlen(szolgnev))==0) {
		sz=kl->szolgok;
		kl->szolgok=kl->szolgok->next;
		torolve=1;
	} else {
		sz=kl->szolgok->next;
		struct szolg* elozo=kl->szolgok;
		while(sz!=NULL){
			if(strncmp(sz->nev,szolgnev,strlen(szolgnev))==0)
				break;
			elozo=sz;
			sz=sz->next;
		}

		if(sz!=NULL){
			elozo->next=sz->next;
			torolve=1;
		}
	}
	if(torolve){
		msg(i,"torles ok\n");
	}
	else
	{
		msg(i,"nem lehet torolni\n");
	}
	kl->szolgszam--;
}

void uzenetfeldolgoz(int i){
	char buff[200];
	memset(buff,'\0',200);
	char ibuff[200];
	memset(ibuff,'\0',200);
	char* delim=" ";
	char* res=NULL;

	int recvB=recv(i,ibuff,sizeof buff,0);
	strncpy(buff,ibuff,strlen(ibuff)-2);

	if (recvB<=0) {
		ktorol(i);
		return;
	} else {
		if (strncmp(buff,"\r\n",2)==0) {
			return;
		}
		if (strncmp(buff,"q",1)==0) {
			ktorol(i);
			return;
		}
		if (strncmp(buff,"r ",2)==0) {
			res=strtok(buff,delim);
			res=strtok(NULL,delim);
			while(res!=NULL){
				szolgfelvesz(i,res);
				res=strtok(NULL,delim);
			}
			return;
		}
		if (strncmp(buff,"d ",2)==0) {
			res=strtok(buff,delim);
			res=strtok(NULL,delim);
			while(res!=NULL){
				szolgtorol(i,res);
				res=strtok(NULL,delim);
			}
			return;
		}
	}
}

void processloop(){
	struct sockaddr sa;
	socklen_t sl=sizeof sa;
	fd_set readfd;
	while(1){
		readfd=master;
		select(fdmax+1,&readfd,NULL,NULL,NULL);
		int i;
		for (i = 0; i <= fdmax; ++i) {
			if (FD_ISSET(i,&readfd)) {
				if (i==listensocket) {
					int ujfd=accept(listensocket,&sa,&sl);
					if (kliensszam<MAXC) {
						kfelvesz(ujfd,sa);
					} else {
						kelutasit(ujfd);
					}

				} else {
					uzenetfeldolgoz(i);
				}
			}
		}
	}
}

void listenserver(){
	struct addrinfo krit;
	struct addrinfo *res;
	memset(&krit,0,sizeof(struct addrinfo));
	krit.ai_family=AF_UNSPEC;
	krit.ai_socktype=SOCK_STREAM;
	krit.ai_flags=AI_PASSIVE;
	getaddrinfo(NULL,PORT,&krit,&res);
	listensocket=socket(res->ai_family,res->ai_socktype,res->ai_protocol);
	int v=1;
	setsockopt(listensocket,SOL_SOCKET,SO_REUSEADDR,&v,sizeof v);
	if(bind(listensocket,res->ai_addr,res->ai_addrlen)){
		printf("binderror\n");
		fflush(stdout);
		exit(-1);
	}
	listen(listensocket,MAXC);

	FD_ZERO(&master);
	FD_SET(listensocket,&master);
	fdmax=listensocket;
}

int main(int argc, char **argv) {
	listenserver();
	processloop();
	return 0;
}
